// Icon implementation for Mac OS X
// Copyright 2006 (C) Ralph Thomas

#include <toolkit/mac/icon-mac.h>
#include <util/resources.h>
#include <algorithm>

namespace bfs = boost::filesystem;

namespace toolkit {
	CGImageRef load( const boost::filesystem::path& p ) {
		std::string path( p.native_file_string() );
		//
		// First get a string of the path, then create a URL to it,
		// then pass the URL to a data provider. How convenient!
		//
		CFStringRef str = CFStringCreateWithBytes( kCFAllocatorDefault,
			(const UInt8*)path.c_str(), path.size(), kCFStringEncodingUTF8,
			false );
		if( !str ) return 0;
		CFURLRef url = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
			str, kCFURLPOSIXPathStyle, false );
		CFRelease( str );
		if( !url ) return 0;
		CGDataProviderRef data = CGDataProviderCreateWithURL( url );
		CFRelease( url );
		if( !data ) return 0;
		//
		// Now actually load the image that we want.
		//
		CGImageRef image = CGImageCreateWithPNGDataProvider( data, NULL, false,
			kCGRenderingIntentDefault );
		CFRelease( data );
		return image;
	}
	//
	/// This class can load and use regular icons.
	//
	class iconMacImpl : public iconMac {
		typedef std::map<iconSize, CGImageRef> iconMap;
		iconMap		_icons;	///< The icons that we have loaded so far.
		std::string	_mime;	///< The MIME type we are looking for.
		//
		/// Release the image in the target.
		///
		/// \param	target	the target containing the image to release.
		//
		static void release( const iconMap::value_type& target ) {
			if( target.second ) CGImageRelease( target.second );
		}
	  public:
		//
		/// Create a new iconMapImpl for the given MIME type.
		///
		/// \param	mime	the MIME type to create the icons for.
		//
		iconMacImpl( const std::string& mime ) : _mime( mime ) {}
		//
		/// Release all allocated icons.
		//
		virtual ~iconMacImpl() {
			std::for_each( _icons.begin(), _icons.end(), &release );
		}
		//
		/// Return an icon of the requested size.
		///
		/// \param	s	the required icon size.
		//
		virtual CGImageRef get( iconSize s ) const {
			//
			// See if we already created this icon.
			//
			iconMap::const_iterator i( _icons.find( s ) );
			if( i != _icons.end() ) return i->second;
			//
			// Find the filename for the icon we need to load.
			//
			std::string filename;
			switch( s ) {
			  case k16x16:
				filename = "16.png";
				break;
			  case k22x22:
				filename = "22.png";
				break;
			  case k32x32:
				filename = "32.png";
				break;
			  case k48x48:
				filename = "48.png";
				break;
			  case k64x64:
				filename = "64.png";
				break;
			  case k128x128:
				filename = "128.png";
				break;
			  default:
				//
				// Unknown icon size.
				//
				return 0;
			}
			bfs::path res = util::getResource( _mime, filename );
			if( res.empty() ) return 0;
			//
			// Load the icon.
			//
			CGImageRef im = load( res );
			if( !im ) return 0;
			//
			// Cache the icon. This is a const call (because
			// this method is a "get", and callers shouldn't have
			// to care if the icon was cached or not).
			//
			const_cast<iconMacImpl*>( this )->_icons[s] = im;
			//
			// Return the icon to the caller.
			//
			return im;
		}
	};
	//
	/// This class is used for holding preview icons.
	//
	class iconPreview : public iconMac {
		CGImageRef	_icon;
	  public:
		iconPreview( CGImageRef image ) : _icon( image ) {}
		virtual ~iconPreview() {
			if( _icon ) CGImageRelease( _icon );
		}
		virtual CGImageRef get( iconSize ) const { return _icon; }
	};
	
	iconMac::~iconMac() {}
	icon::~icon() {}
	icon* icon::create( const bfs::path& file, const std::string& type ) {
		return new iconMacImpl( type );
	}
	icon* icon::preview( const bfs::path& file ) {
		return new iconPreview( load( file ) );
	}
};
